﻿using System;
using System.Collections.Generic;

namespace Notoric.Commanding.CommandBase
{
    /// <summary>
    /// Implements the stack for commands that raises an <see cref="ItemsChanged"/> events when its items has changed.
    /// </summary>
    public class CommandStack : Stack<UndoableCommandBase>
    {
        #region Events

        /// <summary>
        /// Raised when the items of the stack has changed.
        /// </summary>
        public event EventHandler<ItemsChangedEventArgs> ItemsChanged;

        #endregion

        #region Protected Methods

        /// <summary>
        /// Raises the <see cref="ItemsChanged"/> event on this <see cref="CommandStack"/> instance.
        /// </summary>
        protected void RaiseItemsChanged(StackChangeType change)
        {
            if (ItemsChanged != null)
                ItemsChanged(this, new ItemsChangedEventArgs(change));
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Deletes all the commands from the stack.
        /// </summary>
        public new void Clear()
        {
            base.Clear();
            RaiseItemsChanged(StackChangeType.Clear);
        }

        /// <summary>
        /// Gets a value indicating whether the stack contains any items.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="System.Boolean"/>
        /// </para>
        /// <para>
        /// Indicates whether the stack contains any items (false) or is empty (true).
        /// </para>
        /// </value>
        public bool IsEmpty
        {
            get { return this.Count == 0; }
        }

        /// <summary>
        /// Pushes a new command on the top of the stack.
        /// This call raises the <see cref="ItemsChanged"/> event.
        /// </summary>
        /// <param name="item">Command that is inserted on the stack</param>
        public new void Push(UndoableCommandBase item)
        {
            base.Push(item);
            RaiseItemsChanged(StackChangeType.Push);
        }

        /// <summary>
        /// Deletes a command from the top of the stack and returns it.
        /// This call raises the <see cref="ItemsChanged"/> event.
        /// </summary>
        /// <returns>Reference to a <see cref="UndoableCommandBase"/> instance</returns>
        public new UndoableCommandBase Pop()
        {
            UndoableCommandBase cmd = base.Pop();
            RaiseItemsChanged(StackChangeType.Pop);
            return cmd;
        }

        #endregion
    }

    /// <summary>
    /// Describes the change of a stack.
    /// </summary>
    public enum StackChangeType
    {
        /// <summary>
        /// The content of the stack has been deleted.
        /// </summary>
        Clear,
        /// <summary>
        /// A new item has been added to the top of the stack.
        /// </summary>
        Push,
        /// <summary>
        /// An item has been removed from the top of the stack.
        /// </summary>
        Pop
    }

    /// <summary>
    /// Holds the data of a <see cref="CommandStack.ItemsChanged"/> event.
    /// </summary>
    public class ItemsChangedEventArgs : EventArgs
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="ItemsChangedEventArgs"/> instance.
        /// </summary>
        /// <param name="change">
        /// <para>
        /// Type: <see cref="StackChangeType"/>
        /// </para>
        /// <para>
        /// The change that occured.
        /// </para>
        /// </param>
        public ItemsChangedEventArgs(StackChangeType change)
        {
            Change = change;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the change of the stack.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="StackChangeType"/>
        /// </para>
        /// <para>
        /// The change that occured on the stack.
        /// </para>
        /// </value>
        public StackChangeType Change
        {
            get;
            private set;
        }

        #endregion
    }
}
